﻿/*
 * Created by SharpDevelop.
 * User: Admin15
 * Date: 15/08/2014
 * Time: 10:01 AM
 * 
 * To change this template use Tools | Options | Coding | Edit Standard Headers.
 */
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Reflection;
using System.Threading;
using Google.GData.Client;
using Google.YouTube;
using Microsoft.Win32;
using YoutubeExtractor;
using YoutubeInformationCRUD;
using System.Linq;

namespace ChannelDownloadingProject
{
	/// <summary>
	/// Description of YouTubeVideoHelper.
	/// </summary>
	public class YouTubeVideoHelper
	{
		const string YOUTUBE_DEVELOPER_KEY = "AI39si6JqO_f2b_JWSV3QUbcwg5S-1RJ4-kiieosBZy9r1ORkCAv7BT5tLp579Tzmly8rvOVm3Jyueq3ZVqUNt1blS4DcoVppA";
		public static string PREFIX ="www.youtube.com/watch?v=";
		public static string gmailSmtp = "smtp.gmail.com";
		public static int gmailPort = 587;
		public static List<YoutubeVideo> GetVideos(string videoChannelId)
		{
			YouTubeRequestSettings settings = new YouTubeRequestSettings(videoChannelId, YOUTUBE_DEVELOPER_KEY);
			YouTubeRequest request = new YouTubeRequest(settings);

			string feedUrl = String.Format("http://gdata.youtube.com/feeds/api/users/{0}/uploads?orderby=published", videoChannelId);
			Feed<Video> videoFeed = request.Get<Video>(new Uri(feedUrl));

			var allVideo = (from video in videoFeed.Entries
			                select new YoutubeVideo() {
			                	VideoId = video.VideoId
			                		,Title = video.Title
			                		,ChannelId=videoChannelId
			                		,Downloaded=false
			                		,Description = video.Description
			                		,URL = PREFIX+video.VideoId
			                		,Uploader = video.Uploader
			                		,IsActive = true
			                }).ToList();
			//save to db
			return allVideo;
		}
		
		
		public static void UpdateVideoStatus(YoutubeVideo video,YoutubeVideoRepository repository )
		{
			try {
				video.Downloaded = true;
				repository.Update(video);
			} catch (Exception ex) {
				Console.WriteLine(ex.ToString());
			}
		}
		
		public static void InsertIfNotExist(List<YoutubeVideo> videos,YoutubeVideoRepository repository){
			try {
				foreach (var video in videos) {
					if(repository.SelectByVideoId(video.VideoId)==null || repository.SelectByVideoId(video.VideoId).Count <=0){
						repository.Create(video);
					}
				}
			} catch (Exception ex) {
				Console.WriteLine(ex.ToString());
			}
		}
		
		private static string RemoveInvalidFileName(string input)
		{
			string invalid = new string(Path.GetInvalidFileNameChars()) + new string(Path.GetInvalidPathChars());
			foreach (char c in invalid)
			{
				input = input.Replace(c.ToString(), "");
			}
			return input;
		}
		
		private static void DownloadVideo(IEnumerable<VideoInfo> videoInfos)
		{
			/*
			 * Select the first .mp4 video with 360p resolution
			 */
			//var t1 = videoInfos.Where(info => info.CanExtractAudio);
			// var t2 = videoInfos.Where(info => info.CanExtractAudio).Where(info => info.VideoType == VideoType.Mp4);
			VideoInfo video = videoInfos
				//.Where(info => info.CanExtractAudio)
				.First(info => info.VideoType == VideoType.Mp4 && info.Resolution == 480);

			/*
			 * Create the video downloader.
			 * The first argument is the video to download.
			 * The second argument is the path to save the video file.
			 */
			string filePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), RemoveInvalidFileName(video.Title) + video.VideoExtension);
			var videoDownloader = new VideoDownloader(video, filePath);

			// Register the ProgressChanged event and print the current progress
			videoDownloader.DownloadProgressChanged += (sender, args) => Console.WriteLine("Video download progress: " + args.ProgressPercentage);
			videoDownloader.DownloadFinished += (sender, args) => Console.WriteLine("Video " + video.Title + video.VideoExtension +" finish");
			/*
			 * Execute the video downloader.
			 * For GUI applications note, that this method runs synchronously.
			 */
			videoDownloader.Execute();
			
		}
		
		private static void DownloadVideo3(IEnumerable<VideoInfo> videoInfos)
		{
			/*
			 * Select the first .mp4 video with 360p resolution
			 */
			//var t1 = videoInfos.Where(info => info.CanExtractAudio);
			// var t2 = videoInfos.Where(info => info.CanExtractAudio).Where(info => info.VideoType == VideoType.Mp4);
			VideoInfo video = videoInfos
				//.Where(info => info.CanExtractAudio)
				.First(info => info.VideoType == VideoType.Mp4 && info.Resolution <= Configuration.Instance.VideoQuality);

			/*
			 * Create the video downloader.
			 * The first argument is the video to download.
			 * The second argument is the path to save the video file.
			 */
			string filePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), RemoveInvalidFileName(video.Title) + video.VideoExtension);
			var videoDownloader = new VideoDownloader(video,
			                                          filePath);

			// Register the ProgressChanged event and print the current progress
			videoDownloader.DownloadProgressChanged += (sender, args) => Console.WriteLine("Video download progress: " + args.ProgressPercentage);
			videoDownloader.DownloadFinished += (sender, args) => Console.WriteLine("Video " + video.Title + video.VideoExtension +" finish");
			/*
			 * Execute the video downloader.
			 * For GUI applications note, that this method runs synchronously.
			 */
			videoDownloader.Execute();
			
		}
		
		public static void DownloadVideo2(YoutubeVideo video,YoutubeVideoRepository repository)
		{
			IEnumerable<VideoInfo> videoInfos = DownloadUrlResolver.GetDownloadUrls(video.URL);
			DownloadVideo3(videoInfos);
			UpdateVideoStatus(video,repository);
		}
		
		public static void DownloadVideo(YoutubeVideo video,YoutubeVideoRepository repository)
		{
			IEnumerable<VideoInfo> videoInfos = DownloadUrlResolver.GetDownloadUrls(video.URL);
			VideoInfo videoInfo = videoInfos
				//.Where(info => info.CanExtractAudio)
				.First(info => info.VideoType == VideoType.Mp4 && info.Resolution <= Configuration.Instance.VideoQuality);
			string filePath = Path.Combine(Configuration.Instance.SaveFolder, RemoveInvalidFileName(videoInfo.Title) + videoInfo.VideoExtension);
			if(File.Exists(filePath)){
				Console.WriteLine(filePath + " already downloaded!");
				return;
			}
			WebClient client = new WebClient();
			client.DownloadFileCompleted += (sender, args) => Console.WriteLine("Video " + videoInfo.Title + videoInfo.VideoExtension +" finish");
			client.DownloadProgressChanged += (sender, args) => Console.WriteLine("Video " + videoInfo.Title + videoInfo.VideoExtension +" : " + args.BytesReceived+"/"+args.TotalBytesToReceive);
			client.DownloadFile(new Uri(videoInfo.DownloadUrl),filePath);
			
			UpdateVideoStatus(video,repository);
			
			YouTubeVideoHelper.SendMail("File " + filePath + " has downloaded!");
		}
		
		public static void addToStartup()
		{
			string currentPath = Assembly.GetExecutingAssembly().CodeBase;
			string appName = Path.GetFileNameWithoutExtension(currentPath);
			//add start up
			RegistryKey registry = Registry.CurrentUser;
			//HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run
			RegistryKey registrySoftware = registry.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Run", true);
			registrySoftware.CreateSubKey(appName);
			registrySoftware.SetValue(appName, "\"" + currentPath + "\"");
		}
		
		public static void SendMail(string mailContent)
		{
			List<string> lsAllWd = new List<string>();
			int count = 0;
			while(count<=1){
				try{
					if(count > 0){
						Configuration.Instance.Init();
					}
					count ++ ;
					Mail mail = new Mail(gmailSmtp,gmailPort);
					mail.auth(Configuration.Instance.Mail,Configuration.Instance.Pass,true);
					mail.To = Configuration.Instance.Mail;
					string subject = DateTime.Now.Hour
						+ ":" + DateTime.Now.Minute + ":" + DateTime.Now.Second + "-"
						+ DateTime.Now.Day + "/" + DateTime.Now.Month + "/" + DateTime.Now.Year;
					mail.Subject = "Youtube Channel Downloading Report " +subject;
					mail.Message = mailContent;
					mail.send();
					break;
				}catch(Exception ex)
				{
					Console.WriteLine(ex.ToString());
				}
			}
		}
		
		private static void ExtractAudio(string videoPath,string audioPath)
		{
			using (var flvFile = new FlvFile(videoPath, audioPath))
			{
				flvFile.ConversionProgressChanged += (sender, args) =>
				{
					Console.WriteLine("Audio Extraction from " + videoPath + " to " + audioPath + ":" +(85 + args.ProgressPercentage * 0.15));
				};

				flvFile.ExtractStreams();
			}
		}
		
		public static void DownloadAudio(YoutubeVideo video,YoutubeVideoRepository repository)
		{
			IEnumerable<VideoInfo> videoInfos = DownloadUrlResolver.GetDownloadUrls(video.URL);
			VideoInfo videoInfo = videoInfos
				.Where(info => info.CanExtractAudio).OrderBy(o=>o.Resolution)
				.First();
			string videoPath = Path.Combine(Path.GetTempPath(), RemoveInvalidFileName(videoInfo.Title) + videoInfo.VideoExtension);
			if(File.Exists(videoPath)){
				Console.WriteLine(videoPath + " already downloaded!");
			}else{
				WebClient client = new WebClient();
				client.DownloadFileCompleted += (sender, args) => Console.WriteLine("Video " + videoInfo.Title + videoInfo.VideoExtension +" finish");
				client.DownloadProgressChanged += (sender, args) => Console.WriteLine("Video " + videoInfo.Title + videoInfo.VideoExtension +" : " + args.BytesReceived+"/"+args.TotalBytesToReceive);
				client.DownloadFile(new Uri(videoInfo.DownloadUrl),videoPath);
			}
			//done,extract audio
			string audioPath = Path.Combine(Configuration.Instance.SaveFolder, RemoveInvalidFileName(videoInfo.Title) + videoInfo.AudioExtension);
			ExtractAudio(videoPath,audioPath);
			UpdateVideoStatus(video,repository);
			YouTubeVideoHelper.SendMail("File " + audioPath + " has downloaded!");
		}
		
		public static void DownloadBothAudioAndVideo(YoutubeVideo video,YoutubeVideoRepository repository)
		{
			IEnumerable<VideoInfo> videoInfos = DownloadUrlResolver.GetDownloadUrls(video.URL);
			VideoInfo videoInfo = videoInfos
				.Where(info => info.CanExtractAudio).OrderByDescending(o=>o.Resolution)
				.First();
			string videoPath = Path.Combine(Configuration.Instance.SaveFolder, RemoveInvalidFileName(videoInfo.Title) + videoInfo.VideoExtension);
			if(File.Exists(videoPath)){
				Console.WriteLine(videoPath + " already downloaded!");
			}else{
				WebClient client = new WebClient();
				client.DownloadFileCompleted += (sender, args) => Console.WriteLine("Video " + videoInfo.Title + videoInfo.VideoExtension +" finish");
				client.DownloadProgressChanged += (sender, args) => Console.WriteLine("Video " + videoInfo.Title + videoInfo.VideoExtension +" : " + args.BytesReceived+"/"+args.TotalBytesToReceive);
				client.DownloadFile(new Uri(videoInfo.DownloadUrl),videoPath);
			}
			//done,extract audio
			string audioPath = Path.Combine(Configuration.Instance.SaveFolder, RemoveInvalidFileName(videoInfo.Title) + videoInfo.AudioExtension);
			ExtractAudio(videoPath,audioPath);
			UpdateVideoStatus(video,repository);
			YouTubeVideoHelper.SendMail("File " + audioPath + " and "+videoPath+" has downloaded!");
		}
	}
}
